/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let x = add(1, 2);
///
/// ```
fn add(a: i32, b: i32) -> i32 {
    return a + b;
}

fn while_demo() {
    let mut number = 1;
    while number != 4 {
        println!("{}", number);
        number += 1;
    }
    println!("EXIT");
}

fn for_ite() {
    let a = [10, 20, 30, 40, 50];
    for i in a.iter() {
        println!("值为 : {}", i);
    }
}

fn for_str() {
    let a = [10, 20, 30, 40, 50];
    for i in 0..a.len() {
        println!("a[{}] = {}", i, a[i]);
    }
}

/// 身经百战的开发者一定遇到过几次这样的情况：某个循环无法在开头和结尾判断是否继续进行循环，必须在循环体中间某处控制循环的进行。
/// 如果遇到这种情况，我们经常会在一个 while (true) 循环体里实现中途退出循环的操作。
fn loop_demo() {
    let s = ['R', 'U', 'N', 'O', 'O', 'B'];
    let mut i = 0;
    loop {
        let ch = s[i];
        if ch == 'O' {
            break;
        }
        println!("\'{}\'", ch);
        i += 1;
    }
}

/// loop 循环可以通过 break 关键字类似于 return 一样使整个循环退出并给予外部一个返回值。
/// 这是一个十分巧妙的设计，因为 loop 这样的循环常被用来当作查找工具使用，如果找到了某个东西当然要将这个结果交出去：
fn loop_ret() {
    let s = ['R', 'U', 'N', 'O', 'O', 'B'];
    let mut i = 0;
    let location = loop {
        let ch = s[i];
        if ch == 'O' {
            break i;
        }
        i += 1;
    };
    println!(" \'O\' 的索引为 {}", location);
}

fn main() {
    // 变量与数据交互的方式
    // 变量与数据交互方式主要有移动（Move）和克隆（Clone）两种：
    // 多个变量可以在 Rust 中以不同的方式与相同的数据交互：
    let x = 5;
    let y = x;
    println!("move");
    /*
        这个程序将值 5 绑定到变量 x，然后将 x 的值复制并赋值给变量 y。现在栈中将有两个值 5。
        此情况中的数据是"基本数据"类型的数据，不需要存储到堆中，仅在栈中的数据的"移动"方式是直接复制，
        这不会花费更长的时间或更多的存储空间。
        "基本数据"类型有这些：
            所有整数类型，例如 i32 、 u32 、 i64 等。
            布尔类型 bool，值为 true 或 false 。
            所有浮点类型，f32 和 f64。
            字符类型 char。
            仅包含以上类型数据的元组（Tuples）。
    */

    // 如果发生交互的数据在堆中就是另外一种情况：
    let s1 = String::from("hello");
    let s2 = s1;
    // println!("{}, world!", s1); // 错误！s1 已经失效
    println!("数据在堆中，栈中仅是保存指向堆中内存的地址引用");
    /*
        第一步产生一个 String 对象，值为 "hello"。其中 "hello" 可以认为是类似于长度不确定的数据，需要在堆中存储。
        第二步的情况略有不同（这不是完全真的，仅用来对比参考）：
        两个 String 对象在栈中，每个 String 对象都有一个指针指向堆中的 "hello" 字符串。在给 s2 赋值时，只有栈中的数据被复制了，堆中的字符串依然还是原来的字符串。

        当变量超出范围时，Rust 自动调用释放资源函数并清理该变量的堆内存。
        但是 s1 和 s2 都被释放的话堆区中的 "hello" 被释放两次，这是不被系统允许的。
        为了确保安全，在给 s2 赋值时 s1 已经无效了。
    */
    // 没错，在把 s1 的值赋给 s2 以后 s1 将不可以再被使用。下面这段程序是错的： println!("{}, world!", s1); // 错误！s1 已经失效
    // s1 名存实亡。

    // 克隆 clone
    /*
        Rust会尽可能地降低程序的运行成本，所以默认情况下，长度较大的数据存放在堆中，且采用移动的方式进行数据交互。
        但如果需要将数据单纯的复制一份以供他用，可以使用数据的第二种交互方式——克隆。
    */
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {}, s2 = {}", s1, s2);
    println!("s1 == s2: {}", s1 == s2);
    /*
        这里是真的将堆中的 "hello" 复制了一份，所以 s1 和 s2 都分别绑定了一个值，释放的时候也会被当作两个资源。
        当然，克隆仅在需要复制的情况下使用，毕竟复制数据会花费更多的时间。
    */

    // 内存与分配
    /*
        内存和分配
        如果我们定义了一个变量并给它赋予一个值，这个变量的值存在于内存中。这种情况很普遍。但如果我们需要储存的数据长度不确定（比如用户输入的一串字符串），
        我们就无法在定义时明确数据长度，也就无法在编译阶段令程序分配固定长度的内存空间供数据储存使用。
        （有人说分配尽可能大的空间可以解决问题，但这个方法很不文明）。
        这就需要提供一种在程序运行时程序自己申请使用内存的机制——堆。
        这里所讲的所有"内存资源"都指的是堆所占用的内存空间。

        有分配就有释放，程序不能一直占用某个内存资源。因此决定资源是否浪费的关键因素就是资源有没有及时的释放。

        我们把字符串样例程序用 C 语言等价编写：

        {
            char *s = strdup("runoob");
            free(s); // 释放 s 资源
        }
        很显然，Rust 中没有调用 free 函数来释放字符串 s 的资源（我知道这样在 C 语言中是不正确的写法，因为 "runoob" 不在堆中，这里假设它在）。Rust 之所以没有明示释放的步骤是因为在变量范围结束的时候，Rust 编译器自动添加了调用释放资源函数的步骤。

        这种机制看似很简单了：它不过是帮助程序员在适当的地方添加了一个释放资源的函数调用而已。但这种简单的机制可以有效地解决一个史上最令程序员头疼的编程问题。
    */

    //  所有权规则
    //  所有权有以下三条规则：
    //  1.Rust 中的每个值都有一个变量，称为其所有者。
    //  2.一次只能有一个所有者。
    //  3.当所有者不在程序运行范围时，该值将被删除。
    //  这三条规则是所有权概念的基础。
    /*
        {
            // 在声明以前，变量 s 无效
            let s = "runoob";
            // 这里是变量 s 的可用范围
        }
        // 变量范围已经结束，变量 s 无效
    */

    // 涉及函数的所有权机制
    /*
        对于变量来说这是最复杂的情况了。
        如果将一个变量当作函数的参数传给其他函数，怎样安全的处理所有权呢？
    */
    let s = String::from("hello");
    // s 被声明有效

    takes_ownership(s);
    /* s 的值被当作参数传入函数
       所以可以当作 s 已经被移动，从这里开始已经无效
    */

    let x = 5;
    // x 被声明有效
    makes_copy(x);
    /*   x 的值被当作参数传入函数
         但 x 是基本类型，依然有效
         在这里依然可以使用 x 却不能使用 s
         函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
    */
    println!("method over!");
    fn takes_ownership(some_string: String) {
        // 一个 String 参数 some_string 传入，有效
        println!("{}", some_string);
    } // 函数结束, 参数 some_string 在这里释放
    fn makes_copy(some_integer: i32) {
        // 一个 i32 参数 some_integer 传入，有效
        println!("{}", some_integer);
    } // 函数结束, 参数 some_integer 是基本类型, 无需释放

    // 引用与租借
    /*
        引用（Reference）是 C++ 开发者较为熟悉的概念。
        如果你熟悉指针的概念，你可以把它看作一种指针。
        实质上"引用"是变量的间接访问方式。
    */
    let sa = String::from("hello");
    let sb = &sa;
    println!("sa is {}, sb is {}", sa, sb);
    // println!("sa == sb: {}", sa == sb);   这里是不能比较的，sa是一个对象，而sb为sa的引用
    /*
        & 运算符可以取变量的"引用"。
        当一个变量的值被引用时，变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值：
        sb存储的是指向sa的栈中地址
    */

    // 函数引用传递
    let st = String::from("hello");
    let len = calculate_length(&st);
    println!("The length of '{}' is {}.", st, len);
    fn calculate_length(s: &String) -> usize {
        s.len()
    }
    /*
        引用不会获得值的所有权。
        引用只能租借（Borrow）值的所有权。
        引用本身也是一个类型并具有一个值，这个值记录的是别的值所在的位置，但引用不具有所指值的所有权：
        let sm = String::from("hello");
        let sn = &sm;
        let sq = sm;
        println!("{}", sn);
        这段程序不正确：因为 sn 租借的 sm 已经将所有权移动到 sq sn 将无法继续租借使用 sm 的所有权。
    */
    // 如果需要使用 sn 使用该值，必须重新租借：
    let sm = String::from("hello");
    let mut sn = &sm;
    let sq = sm;
    sn = &sq;
    println!("{}", sn);
    /*
        既然引用不具有所有权，即使它租借了所有权，它也只享有使用权（这跟租房子是一个道理）。
        如果尝试利用租借来的权利来修改数据会被阻止：
        let s1 = String::from("run");
        let s2 = &s1;
        println!("{}", s2);
        s2.push_str("oob"); // 错误，禁止修改租借的值
        println!("{}", s2);
        这段程序中 s2 尝试修改 s1 的值被阻止，租借的所有权不能修改所有者的值。
    */
    /*
        当然，也存在一种可变的租借方式，就像你租一个房子，
        如果物业规定房主可以修改房子结构，
        房主在租借时也在合同中声明赋予你这种权利，
        你是可以重新装修房子的：
    */
    let mut s1 = String::from("Let's"); // s1 是可变的
    let s2 = &mut s1; // s2 是可变的引用
    s2.push_str(" go!");
    println!("{}", s2);
    /*这段程序就没有问题了。我们用 &mut 修饰可变的引用类型。*/
    /*
        可变引用与不可变引用相比除了权限不同以外，
        可变引用不允许多重引用，但不可变引用可以：

        let mut s = String::from("hello");
        let r1 = &mut s;
        let r2 = &mut s;
        println!("{}, {}", r1, r2);
        这段程序不正确，因为多重可变引用了 s。
        Rust 对可变引用的这种设计主要出于对并发状态下发生数据访问碰撞的考虑，
        在编译阶段就避免了这种事情的发生。
        由于发生数据访问碰撞的必要条件之一是数据被至少一个使用者写且同时被至
        少一个其他使用者读或写，所以在一个值被可变引用时不允许再次被任何引用。
    */

    // loop 循环
    // loop_demo();
    loop_ret();

    // for loop
    // for 循环是最常用的循环结构，常用来遍历一个线性数据据结构（比如数组）。for 循环遍历数组：
    for_ite();

    // for 通过数组索引访问数组
    for_str();

    // rust loop
    while_demo();

    // rust if else构造类似三元表达式语法
    let a = 3;
    let number = if a > 0 { 1 } else { -1 };
    println!("number 为 {}", number);

    // 条件表达式
    let number = 3;
    if number < 5 {
        println!("条件为 true");
    } else {
        println!("条件为 false");
    }

    let x = 5;

    // 代码块
    let _y = {
        let x = 3;
        x + 1
    };

    println!("x 的值为 : {}", x);
    println!("_y 的值为 : {}", _y);

    println!("{}", add(2, 3));

    fn plus(x: i64, y: i64) -> i64 {
        return x + y;
    }
    println!("plus: x + y = {}", plus(1, 2));

    fn sixsix() -> i64 {
        66
    }
    println!("sixsix= {}", sixsix());

    let mut _a = "We are come in!";
    let _a = "We are come in!2";
    println!("Hello, world! {}", _a);

    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x);

    let _sum = 5 + 10; // 加
    let _difference = 95.5 - 4.3; // 减
    let _product = 4 * 30; // 乘
    let _quotient = 56.7 / 32.2; // 除
    let _remainder = 43 % 5; // 求余

    let mut _flag = true; // boolean
    _flag = false;

    let mut _sum = 0;
    _sum += 1;

    // 元组用()定义
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    // tup.0 等于 500
    // tup.1 等于 6.4
    // tup.2 等于 1
    let (_x, _y, _z) = tup;

    let _b = 1;

    // 数组用[]定义
    let _a = [1, 2, 3, 4, 5];
    // a 是一个长度为 5 的整型数组

    let _b = ["January", "February", "March"];
    // b 是一个长度为 3 的字符串数组

    let _c: [i32; 5] = [1, 2, 3, 4, 5];
    // c 是一个长度为 5 的 i32 数组

    let _d = [3; 5];
    // 等同于 let d = [3, 3, 3, 3, 3];

    let _first = _a[0];
    let _second = _a[1];
    // 数组访问

    // _a[0] = 123; // 错误：数组 a 不可变
    let mut _a = [1, 2, 3];
    _a[0] = 4; // 正确

    // 垂悬引用（Dangling References）
    /*
            这是一个换了个名字的概念，
            如果放在有指针概念的编程语言里它就指的是那种没有实际指向一个真正能访问的数据的指针
            （注意，不一定是空指针，还有可能是已经释放的资源）。
            它们就像失去悬挂物体的绳子，所以叫"垂悬引用"。
             "垂悬引用"在 Rust 语言里不允许出现，如果有，编译器会发现它。
            let reference_to_nothing = dangle();
            fn dangle() -> &String {
                let s = String::from("hello");
                &s
            }
            很显然，伴随着 dangle 函数的结束，其局部变量的值本身没有被当作返回值，被释放了。
            但它的引用却被返回，这个引用所指向的值已经不能确定的存在，故不允许其出现。
        */
    /*
            所有权讨论：
            suisky:  Rust 区别与其他高级语言的重要特征，在于其内存管理的两个特点：
                    （1）变量超出作用域会自动释放。
                        对于简单值类型的栈内存（如int，struct）超出作用域后自动释放，这个逻辑在各个语言都有实现。
                        而对于 new 出来的堆内存，在c/c++中是要手动释放的，在java和dotnet中要委托垃圾回收释放或手动写 dispose 语句释放。
                        而垃圾回收不是实时的，会影响性能。而释放语句总会有懒人忘记写的。
                        而 Rust 对栈内存和堆内存一视同仁，超出作用域一律自动释放。
                        Rust 的这个特点在兼顾性能的情况下、有效的减少了代码量和内存泄漏隐患。
                    （2） “所有权” ：某段内存只能被最后的变量名所有，前面声明过的变量都作废，这有效的避免被多个变量释放的问题，
                        而且该操作是在编译期就可以检查到的，这策略可在编译期就能有效的避免空指针问题。
            
            pior:   这部分对于所有权的设定，看起来很奇怪，其实本质上就是在语言层面禁止了同一个可变数据会有多个变量引用的情况，一旦作为参数传递了，就失去了所有权。
                    赋值给另一个变更也就自动放弃了所有权。从根本上杜绝了并发情景下的数据共享冲突。
                    */

    // Rust Slice（切片）类型
    /*
            切片（Slice）是对数据值的部分引用。
            切片这个名字往往出现在生物课上，我们做样本玻片的时候要从生物体上获取切片，以供在显微镜上观察。
            在 Rust 中，切片的意思大致也是这样，只不过它从数据取材引用。
    */
    // 字符串切片
    // 最简单、最常用的数据切片类型是字符串切片（String Slice）。
    let s = String::from("broadcast");
    let part1 = &s[0..5];
    let part2 = &s[5..9];
    println!("{}={}+{}", s, part1, part2);
    /*
            注：Rust 中的字符串类型实质上记录了字符在内存中的起始位置和其长度，我们暂时了解到这一点。
             使用 .. 表示范围的语法在循环章节中出现过。x..y 表示 [x, y) 的数学含义。.. 两边可以没有运算数：
             ..y 等价于 0..y
             x.. 等价于位置 x 到数据结束
             .. 等价于位置 0 到结束
            注意：到目前为止，尽量不要在字符串中使用非英文字符，因为编码的问题。具体原因会在"字符串"章节叙述。
    */
    // 被切片引用的字符串禁止更改其值：
    let mut s = String::from("Let's go!");
    let slice = &s[6..];
    // s.push_str("yes!"); // 错误
    println!("slice = {}", slice);

    /*
            实际上，到目前为止你一定疑惑为什么每一次使用字符串都要这样写String::from("runoob") ，直接写 "runoob" 不行吗？
            事已至此我们必须分辨这两者概念的区别了。
            在 Rust 中有两种常用的字符串类型：str 和 String。
            str 是 Rust 核心语言类型，就是本章一直在讲的字符串切片（String Slice），常常以引用的形式出现（&str）。
            */
    // 凡是用双引号包括的字符串常量整体的类型性质都是 &str：
    let s = "hello"; // 这里的 s 就是一个 &str 类型的变量。
    let string = String::from("hello");
    println!("str & string");
    /*
            String 类型是 Rust 标准公共库提供的一种数据类型，它的功能更完善——它支持字符串的追加、清空等实用的操作。
            String 和 str 除了同样拥有一个字符开始位置属性和一个字符串长度属性以外String还有一个容量（capacity）属性。
            String 和 str 都支持切片，切片的结果是 &str 类型的数据。
    */
    // 注意：切片结果必须是引用类型，但开发者必须自己明示这一点:
    let slice = &s[0..3];

    // 有一个快速的办法可以将 String 转换成 &str：
    let mut s1 = String::from("hello");
    let s2 = &s1[..];
    println!("string -> &str");

    s1.push_str(" Jane!");
    println!("s1: {}", s1);

    // 非字符串切片
    /*
            除了字符串以外，其他一些线性数据结构也支持切片操作，例如数组：
    */
    let arr = [1, 3, 5, 7, 9];
    let part = &arr[0..3];
    for i in part.iter() {
        println!("{}", i);
    }
    /*
        运行结果：
        1
        3
        5
    */

    // Rust 结构体
    /*
        Rust 中的结构体（Struct）与元组（Tuple）都可以将若干个类型不一定相同的数据捆绑在一起形成整体，
        但结构体的每个成员和其本身都有一个名字，这样访问它成员的时候就不用记住下标了。
        元组常用于非定义的多值传递，而结构体用于规范常用的数据结构。
        结构体的每个成员叫做"字段"。
    */
    // 结构体定义
    struct Site {
        domain: String,
        name: String,
        nation: String,
        found: u32,
    }
    /*
        注意：如果你常用 C/C++，请记住在 Rust 里 struct 语句仅用来定义，不能声明实例，结尾不需要 ; 符号，而且每个字段定义之后用 , 分隔。
    */

    // 结构体实例
    // Rust 很多地方受 JavaScript 影响，在实例化结构体的时候用 JSON 对象的 key: value 语法来实现定义：
    let lqy = Site {
        domain: String::from("www.lqy.com"),
        name: String::from("LQY"),
        nation: String::from("China"),
        found: 2013,
    };

    // 如果正在实例化的结构体有字段名称和现存变量名称一样的，可以简化书写：
    let domain = String::from("www.runoob.com");
    let name = String::from("RUNOOB");
    let runoob = Site {
        domain,  // 等同于 domain : domain,
        name,    // 等同于 name : name,
        nation: String::from("China"),
        found: 2013,
    };

    /*
        元组结构体
        有一种更简单的定义和使用结构体的方式：元组结构体。
        元组结构体是一种形式是元组的结构体。
    */
    // 与元组的区别是它有名字和固定的类型格式。它存在的意义是为了处理那些需要定义类型（经常使用）又不想太复杂的简单数据：
    struct Color(u8, u8, u8);
    struct Point(f64, f64);

    let black = Color(0, 0, 0);
    let origin = Point(0.0, 0.0);

    /*
        "颜色"和"点坐标"是常用的两种数据类型，但如果实例化时写个大括号再写上两个名字就为了可读性牺牲了便捷性，Rust 不会遗留这个问题。
        元组结构体对象的使用方式和元组一样，通过 . 和下标来进行访问：
    */
    println!("black = ({}, {}, {})", black.0, black.1, black.2);
    println!("origin = ({}, {})", origin.0, origin.1);

    /*
        结构体所有权
        结构体必须掌握字段值所有权，因为结构体失效的时候会释放所有字段。
        这就是为什么本章的案例中使用了 String 类型而不使用 &str 的原因。
        但这不意味着结构体中不定义引用型字段，这需要通过"生命周期"机制来实现。
        但现在还难以说明"生命周期"概念，所以只能在后面章节说明。
    */
    // 输出结构体
    // 调试中，完整地显示出一个结构体实例是非常有用的。但如果我们手动的书写一个格式会非常的不方便。
    // 所以 Rust 提供了一个方便地输出一整个结构体的方法：
    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect1 is {:?}", rect1);
    println!("rect1 is {:#?}", rect1);
    /*
        如第一行所示：一定要导入调试库 #[derive(Debug)] ，之后在 println 和 print 宏中就可以用 {:?} 占位符输出一整个结构体：
        rect1 is Rectangle { width: 30, height: 50 }
        如果属性较多的话可以使用另一个占位符 {:#?} 。
        输出：
        rect1 is Rectangle {
            width: 30,
            height: 50,
        }
    */

    // 结构体方法
    /*
        结构体方法
        方法（Method）和函数（Function）类似，只不过它是用来操作结构体实例的。
        如果你学习过一些面向对象的语言，那你一定很清楚函数一般放在类定义里并在函数中用 this 表示所操作的实例。
        Rust 语言不是面向对象的，从它所有权机制的创新可以看出这一点。但是面向对象的珍贵思想可以在 Rust 实现。
        结构体方法的第一个参数必须是 &self，不需声明类型，因为 self 不是一种风格而是关键字。
    */
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
        fn wider(&self, rect: &Rectangle) -> bool {
            self.width > rect.width
        }
    }
    let rect2 = Rectangle { width: 30, height: 50 };
    println!("rect2's area is {}", rect2.area());
    println!("11111");

    /*请注意，在调用结构体方法的时候不需要填写 self ，这是出于对使用方便性的考虑。*/
    // 一个多参数的例子：
    let rect1 = Rectangle { width: 30, height: 50 };
    let rect2 = Rectangle { width: 40, height: 20 };
    println!("{}", rect1.wider(&rect2));

    // 结构体关联函数
    /*
        之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数：它在 impl 块中却没有 &self 参数。
        这种函数不依赖实例，但是使用它需要声明是在哪个 impl 块中的。
        一直使用的 String::from 函数就是一个"关联函数"。
    */
    impl Rectangle {
        fn create(width: u32, height: u32) -> Rectangle {
            Rectangle { width, height }
        }
    }
    let rect = Rectangle::create(30, 50);
    println!("{:#?}", rect);
    println!("rect's area is {}", rect.area());
    println!("rect is wider than rect1: {}", rect.wider(&rect1));
    /*贴士：结构体 impl 块可以写几次，效果相当于它们内容的拼接！*/

    // 单元结构体
    // 结构体可以只作为一种象征而无需任何成员： 
    struct UnitStruct;
    // 我们称这种没有身体的结构体为单元结构体（Unit Struct）。

    // Rust 枚举类
    /*
        枚举类在 Rust 中并不像其他编程语言中的概念那样简单，但依然可以十分简单的使用：
        #[derive(Debug)]
        enum Book {
            Papery, Electronic
        }
        let book = Book::Papery;
        println!("{:?}", book);
    */

    /*
        书分为纸质书（Papery book）和电子书（Electronic book）。
        如果你现在正在开发一个图书管理系统，你需要描述两种书的不同属性（纸质书有索书号，电子书只有 URL），
        你可以为枚举类成员添加元组属性描述：
        #[derive(Debug)]
        enum Book {
            Papery(u32),
            Electronic(String),
        }
        let book = Book::Papery(1001);
        let ebook = Book::Electronic(String::from("url://..."));
        println!("{:?}", book);
        println!("{:?}", ebook);
    */

    // 如果你想为属性命名，可以用结构体语法：
    /*
    #[derive(Debug)]
    enum Book {
        Papery { index: u32 },
        Electronic { url: String },
    }
    let book = Book::Papery{index: 1001};
    println!("{:?}", book);
    */
    /*虽然可以如此命名，但请注意，并不能像访问结构体字段一样访问枚举类绑定的属性。访问的方法在 match 语法中。*/

    // match 语法
    /*
    枚举的目的是对某一类事物的分类，分类的目的是为了对不同的情况进行描述。
    基于这个原理，往往枚举类最终都会被分支结构处理（许多语言中的 switch ）。 
    switch 语法很经典，但在 Rust 中并不支持，
    很多语言摒弃 switch 的原因都是因为 switch 容易存在因忘记添加 break 而产生的串接运行问题，
    Java 和 C# 这类语言通过安全检查杜绝这种情况出现。

    Rust 通过 match 语句来实现分支结构。
    */
    // 先认识一下如何用 match 处理枚举类：
    enum Book {
        Papery { index: u32 },
        Electronic { url: String },
    }

    let book = Book::Papery { index: 1 };
    let ebook = Book::Electronic { url: String::from("http://www.lqy.com") };
    match book {
        Book::Papery { index } => {
            println!("Papery book: {}", index);
        }
        Book::Electronic { url } => {
            println!("E-book: {}", url);
        }
    }
    /*
        match 块也可以当作函数表达式来对待，它也是可以有返回值的：
        match 枚举类实例 {
            分类1 => 返回值表达式,
            分类2 => 返回值表达式,
            ...
        }
        但是所有返回值表达式的类型必须一样！
    */
    // 如果把枚举类附加属性定义成元组，在 match 块中需要临时指定一个名字：
    enum BookC {
        Papery(u32),
        Electronic { url: String },
    }
    let book = BookC::Papery(1001);
    match book {
        BookC::Papery(i) => {
            println!("{}", i);
        }
        BookC::Electronic { url } => {
            println!("{}", url);
        }
    }

    /*
        match 除了能够对枚举类进行分支选择以外，还可以对整数、浮点数、字符和字符串切片引用（&str）类型的数据进行分支选择。
        其中，浮点数类型被分支选择虽然合法，但不推荐这样使用，因为精度问题可能会导致分支错误。
    */
    /*
        对非枚举类进行分支选择时必须注意处理例外情况，即使在例外情况下没有任何要做的事 . 
        例外情况用下划线 _ 表示：
    */
    let t = "abc";
    match t {
        "abc" => println!("Yes"),
        _ => {}
    }

    // Option 枚举类
    /*
        Option 是 Rust 标准库中的枚举类，这个类用于填补 Rust 不支持 null 引用的空白。
        许多语言支持 null 的存在（C/C++、Java），这样很方便，但也制造了极大的问题，
        null 的发明者也承认这一点，"一个方便的想法造成累计 10 亿美元的损失"。
        null 经常在开发者把一切都当作不是 null 的时候给予程序致命一击：毕竟只要出现一个这样的错误，程序的运行就要彻底终止。
        为了解决这个问题，很多语言默认不允许 null，但在语言层面支持 null 的出现（常在类型前面用 ? 符号修饰）。
        Java 默认支持 null，但可以通过 @NotNull 注解限制出现 null，这是一种应付的办法。

        Rust 在语言层面彻底不允许空值 null 的存在，但无奈null 可以高效地解决少量的问题，所以 Rust 引入了 Option 枚举类：
        enum Option<T> {
            Some(T),
            None,
        }
        */
    // 如果你想定义一个可以为空值的类，你可以这样：
    let opt = Option::Some("Let's go!");
    // 如果你想针对 opt 执行某些操作，你必须先判断它是否是 Option::None：
    match opt {
        Option::Some(something) => {
            println!("{}", something);
        }
        Option::None => {
            println!("opt is nothing");
        }
    }

    // 如果你的变量刚开始是空值，你体谅一下编译器，它怎么知道值不为空的时候变量是什么类型的呢？
    // 所以初始值为空的 Option 必须明确类型：
    let opt: Option<&str> = None;
    match opt {
        Some(something) => {
            println!("{}", something);
        }
        None => {
            println!("opt is nothing");
        }
    }
    /*
        这种设计会让空值编程变得不容易，但这正是构建一个稳定高效的系统所需要的。
        由于 Option 是 Rust 编译器默认引入的，在使用时可以省略 Option:: 直接写 None 或者 Some()。
        上述写法可改为：
        let opt: Option<&str> = None;
        match opt {
            Some(something) => {
                println!("{}", something);
            },
            None => {
                println!("opt is nothing");
            }
        }
    */

    // Option 是一种特殊的枚举类，它可以含值分支选择：
    let t = Some(64);
    match t {
        Some(64) => println!("Yes"),
        _ => println!("No"),
    }

    // if let 语法
    let i = 0;
    match i {
        0 => println!("zero"),
        _ => {}
    }

    // if let改写
    let i = 0;
    if let 0 = i {
        println!("zero");
    }
    /*
        if let 语法格式如下：
        if let 匹配值 = 源变量 {
            语句块
        }
        可以在之后添加一个 else 块来处理例外情况。
        if let 语法可以认为是只区分两种情况的 match 语句的"语法糖"（语法糖指的是某种语法的原理相同的便捷替代品）。
        对于枚举类依然适用：
    */
    enum BookD {
        Papery(u32),
        Electronic(String),
    }
    let book = BookD::Electronic(String::from("url"));
    if let BookD::Papery(index) = book {
        println!("Papery {}", index);
    } else {
        println!("Not papery book");
    }

    // Rust 组织管理
    /*
        任何一门编程语言如果不能组织代码都是难以深入的，几乎没有一个软件产品是由一个源文件编译而成的。
        本教程到目前为止所有的程序都是在一个文件中编写的，主要是为了方便学习 Rust 语言的语法和概念。
        对于一个工程来讲，组织代码是十分重要的。
        Rust 中有三个重要的组织概念：箱、包、模块。
    */

    // 箱（Crate）
    /*
        "箱"是二进制程序文件或者库文件，存在于"包"中。
        "箱"是树状结构的，它的树根是编译器开始运行时编译的源文件所编译的程序。
        注意："二进制程序文件"不一定是"二进制可执行文件"，只能确定是是包含目标机器语言的文件，文件格式随编译环境的不同而不同。
    */

    // 包（Package）
    /*
        当我们使用 Cargo 执行 new 命令创建 Rust 工程时，工程目录下会建立一个 Cargo.toml 文件。
        工程的实质就是一个包，包必须由一个 Cargo.toml 文件来管理，该文件描述了包的基本信息以及依赖项。
        一个包最多包含一个库"箱"，可以包含任意数量的二进制"箱"，但是至少包含一个"箱"（不管是库还是二进制"箱"）。
        当使用 cargo new 命令创建完包之后，src 目录下会生成一个 main.rs 源文件，Cargo 默认这个文件为二进制箱的根，
        编译之后的二进制箱将与包名相同。
    */

    // 模块（Module）
    /*
        对于一个软件工程来说，我们往往按照所使用的编程语言的组织规范来进行组织，组织模块的主要结构往往是树。
        Java 组织功能模块的主要单位是类，而 JavaScript 组织模块的主要方式是 function。
        这些先进的语言的组织单位可以层层包含，就像文件系统的目录结构一样。
        Rust 中的组织单位是模块（Module）。
    */
    mod nation {
        mod government {
            fn govern() {}
        }

        mod congress {
            fn legislate() {}
        }

        mod court {
            fn judicial() {}
        }
    }
    /*
        这是一段描述法治国家的程序：
        国家（nation）包括政府（government）、议会（congress）和法院（court），分别有行政、立法和司法的功能。
        我们可以把它转换成树状结构：
        nation
         ├── government
         │ └── govern
         ├── congress
         │ └── legislate
         └── court
           └── judicial
    */
    // 在文件系统中，目录结构往往以斜杠在路径字符串中表示对象的位置，Rust 中的路径分隔符是 :: 。
    /*
        路径分为绝对路径和相对路径。
        绝对路径从 crate 关键字开始描述。
        相对路径从 self 或 super 关键字或一个标识符开始描述。
        例如：
    */
    // crate::nation::government::govern(); //是描述 govern 函数的绝对路径，
    /*现在你可以尝试在一个源程序里定义类似的模块结构并在主函数中使用路径。
    如果你这样做，你一定会发现它不正确的地方：government 模块和其中的函数都是私有（private）的，你不被允许访问它们。*/
    // 相对路径可以表示为：
    // nation::government::govern();

    // 访问权限
    /*
    Rust 中有两种简单的访问权：公共（public）和私有（private）。
    默认情况下，如果不加修饰符，模块中的成员访问权将是私有的。
    如果想使用公共权限，需要使用 pub 关键字。
    对于私有的模块，只有在与其平级的位置或下级的位置才能访问，不能从其外部访问。
    */

    // 实例
    mod nationC {
        pub mod government {
            pub fn govern() {println!("this is govern BU!")}
        }

        mod congress {
            pub fn legislate() {}
        }

        mod court {
            fn judicial() {
                super::congress::legislate(); // 这段程序是能通过编译的。请注意观察 court 模块中 super 的访问方法。
            }
        }
    }
    nationC::government::govern();

    // 如果模块中定义了结构体，结构体除了其本身是私有的以外，其字段也默认是私有的。
    // 所以如果想使用模块中的结构体以及其字段，需要 pub 声明：
    mod back_of_house {
        pub struct Breakfast {
            pub toast: String,
            seasonal_fruit: String,
        }

        impl Breakfast {
            pub fn summer(toast: &str) -> Breakfast {
                Breakfast {
                    toast: String::from(toast),
                    seasonal_fruit: String::from("peaches"),
                }
            }
        }
    }
    pub fn eat_at_restaurant() {
        let mut meal = back_of_house::Breakfast::summer("Rye");
        meal.toast = String::from("Wheat");
        println!("I'd like {} toast please", meal.toast);
    }
    eat_at_restaurant(); // I'd like Wheat toast please

    // 枚举类枚举项可以内含字段，但不具备类似的性质:
    mod SomeModule {
        pub enum Person {
            King {
                name: String
            },
            Quene
        }
    }

    let person = SomeModule::Person::King{
        name: String::from("Blue")
    };
    match person {
        SomeModule::Person::King {name} => {
            println!("{}", name);  // Blue
        }
        _ => {}
    }

    // 难以发现的模块 main函数外面引入mod second;
    println!("This is the main module.");
    println!("{}", second::message());
    /*This is the main module.
    This is the 2nd module.*/


    println!("{}", run());
    /*这段程序能够通过编译。
    因为 use 关键字把 run 标识符导入到了当前的模块下，可以直接使用。
    这样就解决了局部模块路径过长的问题。*/

    println!("{}", bus_run());

    use crate::second::car::bic_run;
    use crate::second::car::bu_run;
    println!("{}", bic_run());
    println!("{}", bu_run());

    // 引用标准库
    // Rust 官方标准库字典：https://doc.rust-lang.org/stable/std/all.html
    // 所有的系统库模块都是被默认导入的，所以在使用的时候只需要使用 use 关键字简化路径就可以方便的使用了。
    println!("{}", (PI / 2.0).sin());

    // panic错误处理
    panic_demo();
    /*
        运行结果：
        thread 'main' panicked at 'error occured', src\exception.rs:19:9
        stack backtrace:
           0: std::panicking::begin_panic
                     at .\C:\Users\18133\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src/panicking.rs:519:12
           1: rust_first::exception::panic_demo
                     at .\src/exception.rs:19:9             //打印异常的定义地点
           2: rust_first::main
                     at .\src/main.rs:951:5
           3: core::ops::function::FnOnce::call_once
                     at .\C:\Users\18133\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\ops/function.rs:227:5
        note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

        很显然，程序并不能如约运行到 println!("Hello, Rust") ，而是在 panic! 宏被调用时停止了运行。
        不可恢复的错误一定会导致程序受到致命的打击而终止运行。

        让我们注视错误输出的两行：
        第一行输出了 panic! 宏调用的位置以及其输出的错误信息。
        第二行是一句提示，翻译成中文就是"通过 `RUST_BACKTRACE=1`/'RUST_BACKTRACE=full' 环境变量运行以显示回溯"。接下来我们将介绍回溯（backtrace）。
    */

    // 可恢复的错误
    /*
    此概念十分类似于 Java 编程语言中的异常。
    实际上在 C 语言中我们就常常将函数返回值设置成整数来表达函数遇到的错误，
    在 Rust 中通过 Result<T, E> 枚举类作返回值来进行异常表达：
    enum Result<T, E> {
        Ok(T),
        Err(E),
    }
    */
    // 在 Rust 标准库中可能产生异常的函数的返回值都是 Result 类型的。
    // 例如：当我们尝试打开一个文件时：
    let f = File::open("hello.txt");
    match f {
        Ok(file) => {
            println!("File opened successfully.");
        },
        Err(err) => {
            println!("Failed to open the file.");
        }
    }
    // 这里 hello.txt 文件不存在，会打印 "Failed to open the file."。
    // 枚举类章节讲到的 if let 语法可以简化 match 语法块：

    // 如果想使一个可恢复错误按不可恢复错误处理，
    // Result 类提供了两个办法：unwrap() 和 expect(message: &str) ：
    let f1 = File::open("hello.txt").unwrap();
    // let f2 = File::open("hello.txt").expect("Failed to open.");
    /*
    这段程序相当于在 Result 为 Err 时调用 panic! 宏。
    两者的区别在于 expect 能够向 panic! 宏发送一段指定的错误信息。
    */
}
mod second;
mod exception;
use crate::exception::panic_demo;

use core::result::Result::Ok;

use std::fs::File;

// use 关键字
// use 关键字能够将模块标识符引入当前作用域：
use crate::second::car::bicycle::run;
// 当然，有些情况下存在两个相同的名称，且同样需要导入，我们可以使用 as 关键字为标识符添加别名：
use crate::second::car::bus::run as bus_run;

// 在学习了本章的概念之后，我们可以轻松的导入系统库来方便的开发程序了：
use std::f64::consts::PI;